package com.darly.dlcommon.json2view;

import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.text.TextUtils;
import android.util.Base64;
import android.view.ViewGroup;

import androidx.annotation.IntDef;
import androidx.annotation.StringDef;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;

/**
 * Created by avocarrot on 11/12/2014.
 * Every Property of a View is a Dynaic Property
 */
public class DynamicProperty {

    /**
     * possible types that we handle
     **/
    protected static final int NO_VALID = 0x00000001;
    protected static final int STRING = 1 << 1;
    protected static final int DIMEN = 0x00000003;
    protected static final int INTEGER = 1 << 2;
    protected static final int FLOAT = 0x00000005;
    protected static final int COLOR = 0x00000006;
    protected static final int REF = 0x00000007;
    protected static final int BOOLEAN = 1 << 3;
    protected static final int BASE64 = 0x00000009;
    protected static final int DRAWABLE = 1 << 4;
    protected static final int JSON  = 0x00000011;


    @IntDef(flag = true,value = {
            NO_VALID,
            STRING,
            DIMEN,
            INTEGER,
            FLOAT,
            COLOR,
            REF,
            BOOLEAN,
            BASE64,
            DRAWABLE,
            JSON
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface TYPE {}

    /**
     * possible property name that we handle
     **/
    protected static final String NO_VALID_S  = "NO_VALID_S" ;
    protected static final String ID  = "ID" ;
    protected static final String LAYOUT_WIDTH  = "LAYOUT_WIDTH" ;
    protected static final String LAYOUT_HEIGHT  = "LAYOUT_HEIGHT" ;
    protected static final String PADDING_LEFT  = "PADDING_LEFT" ;
    protected static final String PADDING_RIGHT  = "PADDING_RIGHT" ;
    protected static final String PADDING_TOP  = "PADDING_TOP" ;
    protected static final String PADDING_BOTTOM  = "PADDING_BOTTOM" ;
    protected static final String PADDING  = "PADDING" ;
    protected static final String LAYOUT_MARGINLEFT  = "LAYOUT_MARGINLEFT" ;
    protected static final String LAYOUT_MARGINRIGHT  = "LAYOUT_MARGINRIGHT" ;
    protected static final String LAYOUT_MARGINTOP  = "LAYOUT_MARGINTOP" ;
    protected static final String LAYOUT_MARGINBOTTOM  = "LAYOUT_MARGINBOTTOM" ;
    protected static final String LAYOUT_MARGIN  = "LAYOUT_MARGIN" ;
    protected static final String BACKGROUND  = "BACKGROUND" ;
    protected static final String ENABLED  = "ENABLED" ;
    protected static final String SELECTED  = "SELECTED" ;
    protected static final String CLICKABLE  = "CLICKABLE" ;
    protected static final String SCALEX  = "SCALEX" ;
    protected static final String SCALEY  = "SCALEY" ;
    protected static final String MINWIDTH  = "MINWIDTH" ;
    protected static final String MINHEIGTH  = "MINHEIGTH" ;
    protected static final String VISIBILITY  = "VISIBILITY" ;
    protected static final String TEXT  = "TEXT" ;
    protected static final String TEXTCOLOR  = "TEXTCOLOR" ;
    protected static final String TEXTSIZE  = "TEXTSIZE" ;
    protected static final String TEXTSTYLE  = "TEXTSTYLE" ;
    protected static final String ELLIPSIZE  = "ELLIPSIZE" ;
    protected static final String MAXLINES  = "MAXLINES" ;
    protected static final String GRAVITY  = "GRAVITY" ;
    protected static final String DRAWABLETOP  = "DRAWABLETOP" ;
    protected static final String DRAWABLEBOTTOM  = "DRAWABLEBOTTOM" ;
    protected static final String DRAWABLELEFT  = "DRAWABLELEFT" ;
    protected static final String DRAWABLERIGHT  = "DRAWABLERIGHT" ;
    protected static final String SRC  = "SRC" ;
    protected static final String SCALETYPE  = "SCALETYPE" ;
    protected static final String ADJUSTVIEWBOUNDS  = "ADJUSTVIEWBOUNDS" ;
    protected static final String LAYOUT_ABOVE  = "LAYOUT_ABOVE" ;
    protected static final String LAYOUT_ALIGNBASELINE  = "LAYOUT_ALIGNBASELINE" ;
    protected static final String LAYOUT_ALIGNBOTTOM  = "LAYOUT_ALIGNBOTTOM" ;
    protected static final String LAYOUT_ALIGNEND  = "LAYOUT_ALIGNEND" ;
    protected static final String LAYOUT_ALIGNLEFT  = "LAYOUT_ALIGNLEFT" ;
    protected static final String LAYOUT_ALIGNPARENTBOTTOM  = "LAYOUT_ALIGNPARENTBOTTOM" ;
    protected static final String LAYOUT_ALIGNPARENTEND  = "LAYOUT_ALIGNPARENTEND" ;
    protected static final String LAYOUT_ALIGNPARENTLEFT  = "LAYOUT_ALIGNPARENTLEFT" ;
    protected static final String LAYOUT_ALIGNPARENTRIGHT  = "LAYOUT_ALIGNPARENTRIGHT" ;
    protected static final String LAYOUT_ALIGNPARENTSTART  = "LAYOUT_ALIGNPARENTSTART" ;
    protected static final String LAYOUT_ALIGNPARENTTOP  = "LAYOUT_ALIGNPARENTTOP" ;
    protected static final String LAYOUT_ALIGNRIGHT  = "LAYOUT_ALIGNRIGHT" ;
    protected static final String LAYOUT_ALIGNSTART  = "LAYOUT_ALIGNSTART" ;
    protected static final String LAYOUT_ALIGNTOP  = "LAYOUT_ALIGNTOP" ;
    protected static final String LAYOUT_ALIGNWITHPARENTIFMISSING  = "LAYOUT_ALIGNWITHPARENTIFMISSING" ;
    protected static final String LAYOUT_BELOW  = "LAYOUT_BELOW" ;
    protected static final String LAYOUT_CENTERHORIZONTAL  = "LAYOUT_CENTERHORIZONTAL" ;
    protected static final String LAYOUT_CENTERINPARENT  = "LAYOUT_CENTERINPARENT" ;
    protected static final String LAYOUT_CENTERVERTICAL  = "LAYOUT_CENTERVERTICAL" ;
    protected static final String LAYOUT_TOENDOF  = "LAYOUT_TOENDOF" ;
    protected static final String LAYOUT_TOLEFTOF  = "LAYOUT_TOLEFTOF" ;
    protected static final String LAYOUT_TORIGHTOF  = "LAYOUT_TORIGHTOF" ;
    protected static final String LAYOUT_TOSTARTOF  = "LAYOUT_TOSTARTOF" ;
    protected static final String LAYOUT_GRAVITY  = "LAYOUT_GRAVITY" ;
    protected static final String LAYOUT_WEIGHT  = "LAYOUT_WEIGHT" ;
    protected static final String SUM_WEIGHT  = "SUM_WEIGHT" ;
    protected static final String ORIENTATION  = "ORIENTATION" ;
    protected static final String TAG  = "TAG" ;
    protected static final String FUNCTION  = "FUNCTION" ;
    @StringDef(value = {
            NO_VALID_S,
            ID,
            LAYOUT_WIDTH,
            LAYOUT_HEIGHT,
            PADDING_LEFT,
            PADDING_RIGHT,
            PADDING_TOP,
            PADDING_BOTTOM,
            PADDING,
            LAYOUT_MARGINLEFT,
            LAYOUT_MARGINRIGHT,
            LAYOUT_MARGINTOP,
            LAYOUT_MARGINBOTTOM,
            LAYOUT_MARGIN,
            BACKGROUND,
            ENABLED,
            SELECTED,
            CLICKABLE,
            SCALEX,
            SCALEY,
            MINWIDTH,
            MINHEIGTH,
            VISIBILITY,
            TEXT,
            TEXTCOLOR,
            TEXTSIZE,
            TEXTSTYLE,
            ELLIPSIZE,
            MAXLINES,
            GRAVITY,
            DRAWABLETOP,
            DRAWABLEBOTTOM,
            DRAWABLELEFT,
            DRAWABLERIGHT,
            SRC,
            SCALETYPE,
            ADJUSTVIEWBOUNDS,
            LAYOUT_ABOVE,
            LAYOUT_ALIGNBASELINE,
            LAYOUT_ALIGNBOTTOM,
            LAYOUT_ALIGNEND,
            LAYOUT_ALIGNLEFT,
            LAYOUT_ALIGNPARENTBOTTOM,
            LAYOUT_ALIGNPARENTEND,
            LAYOUT_ALIGNPARENTLEFT,
            LAYOUT_ALIGNPARENTRIGHT,
            LAYOUT_ALIGNPARENTSTART,
            LAYOUT_ALIGNPARENTTOP,
            LAYOUT_ALIGNRIGHT,
            LAYOUT_ALIGNSTART,
            LAYOUT_ALIGNTOP,
            LAYOUT_ALIGNWITHPARENTIFMISSING,
            LAYOUT_BELOW,
            LAYOUT_CENTERHORIZONTAL,
            LAYOUT_CENTERINPARENT,
            LAYOUT_CENTERVERTICAL,
            LAYOUT_TOENDOF,
            LAYOUT_TOLEFTOF,
            LAYOUT_TORIGHTOF,
            LAYOUT_TOSTARTOF,
            LAYOUT_GRAVITY,
            LAYOUT_WEIGHT,
            SUM_WEIGHT,
            ORIENTATION,
            TAG,
            FUNCTION
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface NAME {}




    public @NAME String  name;
    public @TYPE int type;
    private Object value;

    /**
     * @param v value to convert as string
     * @return Value as object depends on the type
     */
    private Object convertValue(Object v) {
        if (v == null)
            return null;
        switch (type) {
            case INTEGER: {
                return Integer.parseInt(v.toString());
            }
            case FLOAT: {
                return Float.parseFloat(v.toString());
            }
            case DIMEN: {
                return convertDimenToPixel(v.toString());
            }
            case COLOR: {
                return convertColor(v.toString());
            }
            case BOOLEAN: {
                String value = v.toString();
                if (value.equalsIgnoreCase("t")) {
                    return true;
                } else if (value.equalsIgnoreCase("f")) {
                    return false;
                } else if (value.equalsIgnoreCase("true")) {
                    return true;
                } else if (value.equalsIgnoreCase("false")) {
                    return false;
                }
                return Integer.parseInt(value) == 1;
            }
            case BASE64: {
                try {
                    InputStream stream = new ByteArrayInputStream(Base64.decode(v.toString(), Base64.DEFAULT));
                    return BitmapFactory.decodeStream(stream);
                } catch (Exception e) {
                    return null;
                }
            }
            case DRAWABLE: {
                JSONObject drawableProperties = (JSONObject) v;

                GradientDrawable gd = new GradientDrawable();

                if (drawableProperties != null) {

                    try {
                        gd.setColor(convertColor(drawableProperties.getString("COLOR")));
                    } catch (JSONException e) {
                    }
                    if (drawableProperties.has("CORNER")) {
                        String cornerValues = null;
                        try {
                            cornerValues = drawableProperties.getString("CORNER");
                        } catch (JSONException e) {
                        }
                        if (!TextUtils.isEmpty(cornerValues)) {
                            if (cornerValues.contains("|")) {
                                float[] corners = new float[8];
                                Arrays.fill(corners, 0);
                                String[] values = cornerValues.split("\\|");
                                int count = Math.min(values.length, corners.length);
                                for (int i = 0; i < count; i++) {
                                    try {
                                        corners[i] = convertDimenToPixel(values[i]);
                                    } catch (Exception e) {
                                        corners[i] = 0f;
                                    }
                                }
                                gd.setCornerRadii(corners);
                            } else {
                                try {
                                    gd.setCornerRadius(convertDimenToPixel(cornerValues));
                                } catch (Exception e) {
                                    gd.setCornerRadius(0f);
                                }
                            }
                        }

                    }
                    int strokeColor = 0x00FFFFFF;
                    int strokeSize = 0;
                    if (drawableProperties.has("STROKECOLOR")) {
                        try {
                            strokeColor = convertColor(drawableProperties.getString("STROKECOLOR"));
                        } catch (JSONException e) {
                        }
                    }
                    if (drawableProperties.has("STROKESIZE")) {
                        try {
                            strokeSize = (int) convertDimenToPixel(drawableProperties.getString("STROKESIZE"));
                        } catch (JSONException e) {
                        }
                    }
                    gd.setStroke(strokeSize, strokeColor);

                }

                return gd;
            }
        }
        return v;
    }

    /**
     * create property and parse json
     *
     * @param jsonObject : json to parse
     */
    public DynamicProperty(JSONObject jsonObject) {
        super();
        try {
            name = jsonObject.getString("name").toUpperCase().trim();
        } catch (Exception e) {
            name = NO_VALID_S;
        }
        try {
            type = jsonObject.getInt("type");
        } catch (Exception e) {
            type = NO_VALID;
        }
        try {
            value = convertValue(jsonObject.get("value"));
        } catch (Exception e) {
        }
    }

    public boolean isValid() {
        return value != null;
    }

    /**
     * @param clazz
     * @param varName
     * @return search in clazz of possible variable name (varName) and return its value
     */
    public Object getValueInt(Class clazz, String varName) {

        java.lang.reflect.Field fieldRequested = null;

        try {
            fieldRequested = clazz.getField(varName);
            if (fieldRequested != null) {
                return fieldRequested.get(clazz);
            }
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * next function just cast value and return the object
     **/

    public int getValueColor() {
        if (type == COLOR) return Integer.class.cast(value);
        return -1;
    }

    public String getValueString() {
        return String.class.cast(value);
    }

    public int getValueInt() {
        if (value instanceof Integer)
            return Integer.class.cast(value);
        else if (value instanceof Float)
            return (int) getValueFloat();
        else
            return (int) value;
    }

    public float getValueFloat() {
        return Float.class.cast(value);
    }

    public Boolean getValueBoolean() {
        return Boolean.class.cast(value);
    }

    public Bitmap getValueBitmap() {
        return (Bitmap) value;
    }

    public Drawable getValueBitmapDrawable() {
        return new BitmapDrawable(Resources.getSystem(), getValueBitmap());
    }

    public Drawable getValueGradientDrawable() {
        return (Drawable) value;
    }

    public JSONObject getValueJSON() {
        return JSONObject.class.cast(value);
    }


    int convertColor(String color) {
        if (color.startsWith("0x")) {
            return (int) Long.parseLong(color.substring(2), 16);
        }
        return Color.parseColor(color);
    }

    float convertDimenToPixel(String dimen) {
        if (dimen.endsWith("dp"))
            return DynamicHelper.dpToPx(Float.parseFloat(dimen.substring(0, dimen.length() - 2)));
        else if (dimen.endsWith("sp"))
            return DynamicHelper.spToPx(Float.parseFloat(dimen.substring(0, dimen.length() - 2)));
        else if (dimen.endsWith("px"))
            return Integer.parseInt(dimen.substring(0, dimen.length() - 2));
        else if (dimen.endsWith("%"))
            return (int) (Float.parseFloat(dimen.substring(0, dimen.length() - 1)) / 100f * DynamicHelper.deviceWidth());
        else if (dimen.equalsIgnoreCase("match_parent"))
            return ViewGroup.LayoutParams.MATCH_PARENT;
        else if (dimen.equalsIgnoreCase("wrap_content"))
            return ViewGroup.LayoutParams.WRAP_CONTENT;
        else
            return Integer.parseInt(dimen);
    }

}
